
import json
import time
import pprint
import re
from openpyxl import Workbook
import typing
import traceback
from typing import Union, Any, AnyStr
import datetime
import inspect
_regexs = {}

#TODO 导出
def batch_excel(datas: dict, output_file: str):
    if isinstance(datas, str):
        datas = get_json(datas)

    wb = Workbook()
    for key, values in datas.items():
        ws = wb.create_sheet(title=key)  # 创建以 key 为名称的表单
        if isinstance(values, list) and all(isinstance(row, list) for row in values):
            # 如果 values 是二维数组
            for row in values:
                ws.append(row)  # 将每一行数据添加到表单中
        elif isinstance(values, list):
            # 如果 values 是一维数组
            ws.append(values)  # 将一维数组作为一行数据添加到表单中
        else:
            raise ValueError(f"Unsupported data type for key '{key}'")  # 不支持的数据类型

    wb.save(output_file)
def get_json(json_str):
    """
    @summary: 取json对象
    ---------
    @param json_str: json格式的字符串
    ---------
    @result: 返回json对象
    """

    try:
        return json.loads(json_str) if json_str else {}
    except Exception as e1:
        try:
            json_str = json_str.strip()
            json_str = json_str.replace("'", '"')
            keys = get_info(json_str, r"(\w+):")
            for key in keys:
                json_str = json_str.replace(key, '"%s"' % key)

            return json.loads(json_str) if json_str else {}

        except Exception as e2:
            print(e2)

        return {}
def get_info(html, regexs, allow_repeat=True, fetch_one=False, split=None):
    regexs = isinstance(regexs, str) and [regexs] or regexs

    infos = []
    for regex in regexs:
        if regex == "":
            continue

        if regex not in _regexs.keys():
            _regexs[regex] = re.compile(regex, re.S)

        if fetch_one:
            infos = _regexs[regex].search(html)
            if infos:
                infos = infos.groups()
            else:
                continue
        else:
            infos = _regexs[regex].findall(str(html))

        if len(infos) > 0:
            # print(regex)
            break

    if fetch_one:
        infos = infos if infos else ("",)
        return infos if len(infos) > 1 else infos[0]
    else:
        infos = allow_repeat and infos or sorted(set(infos), key=infos.index)
        infos = split.join(infos) if split else infos
        return infos

def with_open(file_path, mode="r", encoding="UTF-8", ignore: bool = False) -> AnyStr:
    if 'b' in mode:  # 二进制文件不允许编码
        encoding = None

    with open(file_path, mode=mode, encoding=encoding) as fp:
        file = fp.read()

    if ignore:
        return file.encode('gb2312', 'ignore').decode('gb2312')
    else:
        return file

def json_loads(obj) -> Union[dict, list]:
    if isinstance(obj, (dict, list)):
        return obj

    return json.loads(obj)
def with_save(file_path, content, mode="wb", encoding="utf-8") -> bool:
    try:
        if 'b' in mode:
            encoding = None

        if isinstance(content, str):
            mode = "w"

        if isinstance(content, (list, dict)):
            mode = 'w'
            content = json_dumps(content, indent=0)

        with open(file_path, mode, encoding=encoding) as fp:
            fp.write(content)
    except Exception as e:
        print(e)
        return False
    else:
        print(f"{file_path} 保存成功")
        return True

def make_insert_sql(
        table, data, auto_update=False, update_columns=(), insert_ignore=False
):
    """
    @summary: 适用于mysql， oracle数据库时间需要to_date 处理（TODO）
    ---------
    @param table:
    @param data: 表数据 json格式
    @param auto_update: 使用的是replace into， 为完全覆盖已存在的数据
    @param update_columns: 需要更新的列 默认全部，当指定值时，auto_update设置无效，当duplicate key冲突时更新指定的列
    @param insert_ignore: 数据存在忽略
    ---------
    @result:
    """

    keys = ["`{}`".format(key) for key in data.keys()]
    keys = list2str(keys).replace("'", "")

    values = [format_sql_value(value) for value in data.values()]
    values = list2str(values)

    if update_columns:
        if not isinstance(update_columns, (tuple, list)):
            update_columns = [update_columns]
        update_columns_ = ", ".join(
            ["{key}=values({key})".format(key=key) for key in update_columns]
        )
        sql = (
                "insert%s into `{table}` {keys} values {values} on duplicate key update %s"
                % (" ignore" if insert_ignore else "", update_columns_)
        )

    elif auto_update:
        sql = "replace into `{table}` {keys} values {values}"
    else:
        sql = "insert%s into `{table}` {keys} values {values}" % (
            " ignore" if insert_ignore else ""
        )

    sql = sql.format(table=table, keys=keys, values=values).replace("None", "null")
    return sql

def list2str(datas):
    """
    列表转字符串
    :param datas: [1, 2]
    :return: (1, 2)
    """
    data_str = str(tuple(datas))
    data_str = re.sub(r",\)$", ")", data_str)
    return data_str

def format_sql_value(value):
    if isinstance(value, str):
        value = value.strip()

    elif isinstance(value, (list, dict)):
        value = dumps_json(value, indent=None)

    elif isinstance(value, (datetime.date, datetime.time)):
        value = str(value)

    elif isinstance(value, bool):
        value = int(value)

    return value
def dumps_json(data, indent: Any = 4, sort_keys=False):
    """
    @summary: 格式化json 用于打印
    ---------
    @param data: json格式的字符串或json对象
    @param indent:
    @param sort_keys:
    ---------
    @result: 格式化后的字符串
    """
    try:
        if isinstance(data, str):
            data = get_json(data)

        data = json.dumps(
            data,
            ensure_ascii=False,
            indent=indent,
            skipkeys=True,
            sort_keys=sort_keys,
            default=str,
        )

    except Exception as e:
        print(e)
        data = pprint.pformat(data)

    return data

def make_batch_sql(
        table, datas, auto_update=False, update_columns=(), update_columns_value=()
) -> typing.Union[typing.Tuple[str, list], None]:
    """
    @summary: 生产批量的sql
    ---------
    @param table:
    @param datas: 表数据 [{...}]
    @param auto_update: 使用的是replace into， 为完全覆盖已存在的数据
    @param update_columns: 需要更新的列 默认全部，当指定值时，auto_update设置无效，当duplicate key冲突时更新指定的列
    @param update_columns_value: 需要更新的列的值 默认为datas里边对应的值, 注意 如果值为字符串类型 需要主动加单引号， 如 update_columns_value=("'test'",)
    ---------
    @result:
    """
    if not datas:
        return

    keys = list(set([key for data in datas for key in data]))
    values_placeholder = ["%s"] * len(keys)

    values = []
    for data in datas:
        value = []
        for key in keys:
            current_data = data.get(key)
            current_data = format_sql_value(current_data)

            value.append(current_data)

        values.append(value)

    keys = ["`{}`".format(key) for key in keys]
    keys = list2str(keys).replace("'", "")

    values_placeholder = list2str(values_placeholder).replace("'", "")

    if update_columns:
        if not isinstance(update_columns, (tuple, list)):
            update_columns = [update_columns]
        if update_columns_value:
            update_columns_ = ", ".join(
                [
                    "`{key}`={value}".format(key=key, value=value)
                    for key, value in zip(update_columns, update_columns_value)
                ]
            )
        else:
            update_columns_ = ", ".join(
                ["`{key}`=values(`{key}`)".format(key=key) for key in update_columns]
            )
        sql = "insert into `{table}` {keys} values {values_placeholder} on duplicate key update {update_columns}" \
            .format(
            table=table,
            keys=keys,
            values_placeholder=values_placeholder,
            update_columns=update_columns_,
        )
    elif auto_update:
        sql = "replace into `{table}` {keys} values {values_placeholder}".format(
            table=table, keys=keys, values_placeholder=values_placeholder
        )
    else:
        sql = "insert ignore into `{table}` {keys} values {values_placeholder}".format(
            table=table, keys=keys, values_placeholder=values_placeholder
        )

    return sql, values

def make_update_sql(table, data, condition):
    """
    @summary: 适用于mysql， oracle数据库时间需要to_date 处理（TODO）
    ---------
    @param table:
    @param data: 表数据 json格式
    @param condition: where 条件
    ---------
    @result:
    """
    key_values = []

    for key, value in data.items():
        value = format_sql_value(value)
        if isinstance(value, str):
            key_values.append("`{}`={}".format(key, repr(value)))
        elif value is None:
            key_values.append("`{}`={}".format(key, "null"))
        else:
            key_values.append("`{}`={}".format(key, value))

    key_values = ", ".join(key_values)

    sql = "update `{table}` set {key_values} where {condition}"
    sql = sql.format(table=table, key_values=key_values, condition=condition)
    return sql


def print_parent_caller(level: int = 1, types: bool = False):
    """
    打印函数父级调用者的装饰器
    Arg:
        level: 打印父级调用者 level=1, 打印祖父级调用者 level=2
        types:
    """

    def _print_parent_caller(func):
        def wrapper(*args, **kwargs):
            if types:
                frames = inspect.getouterframes(inspect.currentframe(), 2)
                if len(frames) > level:
                    parent_frame = frames[level]  # 直属父级调用者帧对象
                    parent_name = parent_frame[3]  # 直属父级调用者函数名
                    parent_line = parent_frame[2]  # 直属父级调用者行号
                    parent_file = parent_frame[1]  # 直属父级调用者文件路径

                else:
                    pass
            else:
                stack = traceback.extract_stack()
                for frame in stack[:-1]:
                    frame_file, frame_line, frame_func, _ = frame
            return func(*args, **kwargs)

        return wrapper

    return _print_parent_caller

def get_current_timestamp(msec: bool = False):
    if msec:
        return int(time.time() * 1000)
    else:
        return int(time.time())

def json_dumps(data, indent=None, sort_keys=False):
    if not data:
        return None

    elif not indent:
        # 最紧凑的json字符串
        return json.dumps(data, sort_keys=sort_keys, separators=(',', ':'))

    else:
        return json.dumps(data, indent=indent, sort_keys=sort_keys)
